#ifndef WIN32
#include <signal.h>
#include <sys/types.h>       
#include <sys/param.h>       
#include <sys/ioctl.h>       
#include <sys/socket.h>       
#include <net/if.h>       
#include <netinet/in.h>       
#include <net/if_arp.h>       
#include <arpa/inet.h>   
#endif
#include <vector>
#include <string>
#include "miniutil/log.h"
#include "miniutil/strutil.h"
#include "miniutil/socketcmn.h"

namespace miniutil { namespace socketcmn {

	//int block,  0 non block, 1 block
int set_block_mode(SOCKET fd, int block)
{
#ifdef	_WIN32
	unsigned long on = !block;

	return (ioctlsocket(fd, FIONBIO, &on));
#else
	int	flags, retval = 0;

	if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 
	{
		//VERROR("[set_block_mode]nonblock: fcntl(%d, F_GETFL): %s", 
		//	fd,strerror(errno));
		retval--;
	} 
	else 
	{
		if (block)
			flags &= ~O_NONBLOCK;
		else
			flags |= O_NONBLOCK;
		
		/* Apply the mode */
		if (fcntl(fd, F_SETFL, flags) != 0) 
		{
			//VERROR( 
			//	"[set_block_mode]nonblock: fcntl(%d, F_SETFL): %s",
			//    fd, strerror(errno));
			retval--;
		}
	}
	return (retval);
#endif /* _WIN32 */
}

int set_recv_buf(SOCKET fd, int bufsize)
{
    int level = SOL_SOCKET;
    int optnm = SO_RCVBUF;
    char* optvle = (char*)&bufsize;
    int optlen = sizeof(bufsize);
    return setsockopt(fd, level, optnm, optvle, optlen);
}

int set_send_buf(SOCKET fd, int bufsize)
{
    int level = SOL_SOCKET;
    int optnm = SO_SNDBUF;
    char* optvle = (char*)&bufsize;
    int optlen = sizeof(bufsize);
    return setsockopt(fd, level, optnm, optvle, optlen);
}

//para style:192.168.1.1:2223
//1 block 0 none block
SOCKET try_connect(std::string ipstring, int block)
{
	std::vector<std::string> ipstr = miniutil::strutil::split(ipstring, ":");
	if(ipstr.size() != 2)
		return INVALID_SOCKET;

	sockaddr_in addr;
	memset(&addr, 0x00, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(ipstr[0].c_str());
	addr.sin_port = htons(atoi(ipstr[1].c_str()));

	return try_connect(&addr, block);
}

SOCKET try_connect(struct sockaddr_in *paddr, int block)
{
	int ret;
	SOCKET soc = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
	if(soc == INVALID_SOCKET)
	{
		check_cursoc_error();
		return INVALID_SOCKET;
	}
	miniutil::socketcmn::set_block_mode(soc, block);
	ret = ::connect(soc, (sockaddr*)paddr, sizeof(sockaddr_in));
	if(ret == -1)
	{
		ret = check_cursoc_error();
		#ifdef WIN32
		if(ret != WSAEWOULDBLOCK)
		#else
		if(ret != EWOULDBLOCK && ret != EINPROGRESS)
		#endif
		{
			closesocket(soc);
			return INVALID_SOCKET;
		}
	}
	return soc;
}

//0 nonblock 1 block
SOCKET try_listen(unsigned short port, int block)
{
	SOCKET sock;
	int on = 1;
	int len;
	struct sockaddr_in	sin;
	//VDEBUG(D_INFO,"[soccm_listenconn]listen at port: %d\n",port);

	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == INVALID_SOCKET)
	{
		check_cursoc_error();
		//VERROR( "[soccm_listenconn]create socket error\n");
		return sock;
	}

	/* Make it nonblock to avoid block in accept() */
	//ioctlsocket(sock, FIONBIO, &on);
	set_block_mode((int)sock, block);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,(char *) &on, sizeof(on));
	
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = INADDR_ANY;
	len = sizeof(sin);

	if (bind(sock, (struct sockaddr*)&sin, len) == INVALID_SOCKET)
	{
		check_cursoc_error();
		//VERROR( "[soccm_listenconn]Bind error: Port(%d)\n", port);
		closesocket(sock);
		return INVALID_SOCKET;
	}

	if(listen(sock,16) == -1)
	{
		check_cursoc_error();
		//VERROR("[soccm_listenconn]Listen error: Port(%d)\n", port);
		closesocket(sock);
		return INVALID_SOCKET;
	}
	return (sock);
}

int is_public_ip(struct sockaddr_in* paddr)
{

	unsigned int myip;
	myip = htonl(paddr->sin_addr.s_addr);
	return is_public_ip(myip);
}

int is_public_ip(unsigned int myip)
{
	unsigned int ipmin;
	unsigned int ipmax;
	int i;
	const char *privateip[][2] = 
	{
		{"10.0.0.1","10.255.255.255"},
		{"127.0.0.1","127.255.255.255"},
		{"169.254.0.1","169.254.255.255"},
		{"172.16.0.1", "172.31.255.255"},
		{"192.168.0.1", "192.168.255.255"},
		{"224.0.0.0", "239.255.255.255"}
	};

	if(myip == 0x00)
		return SOCCM_INVALID_IP;
	for(i=0; i < 6; i++)
	{
		ipmin = ntohl(inet_addr(privateip[i][0]));
		ipmax = ntohl(inet_addr(privateip[i][1]));
		if(myip >= ipmin &&
			myip <= ipmax)
			return SOCCM_PRIVATE_IP;
	}
	return SOCCM_PUBLIC_IP;
}

//net sequence
static std::string inet4_ntoa_own(unsigned int ip)
{
    std::stringstream ss;
    char* pip = (char*)&ip;
    ss<<(int)(pip[0]&0xff)<<"."<<(pip[1]&0xff)<<"."<<(pip[2]&0xff)<<"."<<(int)(pip[3]&0xff);
    return ss.str();
}

std::string inet_ntoa_safe(struct in_addr in)
{
    //static miniutil::mutex mtx;
    //miniutil::auto_lock kk(&mtx);
    //std::string rtnstr = inet_ntoa(in);
    std::string rtnstr = inet4_ntoa_own(in.s_addr);
    return rtnstr;
}

std::string ip_i2s(struct sockaddr_in addr)
{
    //std::string ipstr = inet_ntoa_safe(addr.sin_addr);
    std::string ipstr = inet4_ntoa_own(addr.sin_addr.s_addr);
    std::stringstream ss;
    ss<<ipstr<<":"<<ntohs(addr.sin_port);
    return ss.str();
}

std::string ip_i2s(unsigned int ip, unsigned short port)
{
	//sockaddr_in addr;
	//memset(&addr, 0x00, sizeof(addr));
	//addr.sin_family =  AF_INET;
	//addr.sin_addr.s_addr = htonl(ip);
	
    std::stringstream ss;
    //ss<<inet_ntoa_safe(addr.sin_addr)<<":"<<port;
    ss<<inet4_ntoa_own(htonl(ip))<<":"<<port;
    return ss.str();

}

std::string get_soc_errorstr(int err)
{
    std::string errstr;

#define CHKERR(name) \
	case(name): \
	{ \
		errstr = #name;  \
		break; \
	}
	switch(err)
	{
#ifdef WIN32
		CHKERR(WSANOTINITIALISED)
		CHKERR(WSAENETDOWN)
		CHKERR(WSAEINTR)
		CHKERR(WSAEINPROGRESS)
		CHKERR(WSAEALREADY)
		CHKERR(WSAEADDRNOTAVAIL)
		CHKERR(WSAEAFNOSUPPORT)
		CHKERR(WSAECONNREFUSED)
		CHKERR(WSAEFAULT)
		CHKERR(WSAEINVAL)
		CHKERR(WSAEISCONN)
		CHKERR(WSAENETUNREACH)
		CHKERR(WSAENOBUFS)
		CHKERR(WSAENOTSOCK)
		CHKERR(WSAETIMEDOUT)
		CHKERR(WSAEWOULDBLOCK )
		CHKERR(WSAEACCES)
		CHKERR(WSAENETRESET)
		CHKERR(WSAENOTCONN)
		CHKERR(WSAEOPNOTSUPP)
		CHKERR(WSAESHUTDOWN)
		CHKERR(WSAEMSGSIZE)
		CHKERR(WSAEHOSTUNREACH)
		CHKERR(WSAECONNABORTED)
		CHKERR(WSAECONNRESET)
		CHKERR(WSA_IO_PENDING)
#else
		CHKERR(EBADF)
		CHKERR(ENETDOWN)
		CHKERR(EINTR)
		CHKERR(EINPROGRESS)
		CHKERR(EALREADY)
		CHKERR(EADDRNOTAVAIL)
		CHKERR(EAFNOSUPPORT)
		CHKERR(ECONNREFUSED)
		CHKERR(EFAULT)
		CHKERR(EINVAL)
		CHKERR(EISCONN)
		CHKERR(ENETUNREACH)
		CHKERR(ENOBUFS)
		CHKERR(ENOTSOCK)
		CHKERR(ETIMEDOUT)
		CHKERR(EWOULDBLOCK )
		//CHKERR(EAGAIN )
		CHKERR(EACCES)
		CHKERR(ENETRESET)
		CHKERR(ENOTCONN)
		CHKERR(EOPNOTSUPP)
		CHKERR(ESHUTDOWN)
		CHKERR(EMSGSIZE)
		CHKERR(EHOSTUNREACH)
		CHKERR(ECONNABORTED)
		CHKERR(ECONNRESET)
        CHKERR(EPIPE)
#endif
	default:
		//VDEBUG(D_INFO, "%s","Unknow Error\n"); 
		break;
    }
#undef CHKERR
    return errstr;
}

int check_cursoc_error()
{
    int err;
#ifdef WIN32
    err = WSAGetLastError();
#else
    err = errno;
#endif
    std::string serr = get_soc_errorstr(err);
#ifdef WIN32
    if(err != WSAEWOULDBLOCK)
#endif
    M_DEBUG("%s\n", serr.c_str());
    return err;
}


int getalllocalips(std::vector<struct in_addr>& rtnvec)
{
#ifdef WIN32
	const int iChkIpTimes = 26; 
	int localIPnums = 0 , i; 
	char hostName[80]; 
	LPHOSTENT lpHostEnt; 
	struct in_addr addr; 
	std::vector<struct sockaddr_in> vusableaddr;

	{WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
	
	memset(&addr, 0, sizeof(struct in_addr)); 
	
	if ( gethostname(hostName, sizeof(hostName)) == SOCKET_ERROR ) 
	{
		//VDEBUG(D_INFO, "gethostname error!\n");
		get_soc_errorstr(WSAGetLastError());
		WSACleanup(); 
		return (-1);
	} 
	//VDEBUG(D_INFO, "Local host name:%s\n", hostName);
	lpHostEnt = gethostbyname( hostName ); 
	if ( !lpHostEnt ) 
	{ 
		//VDEBUG(D_INFO, "Bad host lookup!");
		get_soc_errorstr(WSAGetLastError());
		WSACleanup(); 
		return (-1);
	} 
	for (i=0; i<iChkIpTimes; i++) 
	{ 
		if ( lpHostEnt->h_addr_list[i] == NULL )  
		{ 
			localIPnums = i; 
			break; 
		} 
		memcpy(&addr, lpHostEnt->h_addr_list[i], sizeof(struct in_addr)); 
		rtnvec.push_back(addr);
	}
	WSACleanup(); 
	return (0);
#elif defined __APPLE__
	FILE * p_ifconfig = NULL ;
	char linebuf[1024];

	p_ifconfig = popen( "ifconfig" , "r" );
	if ( p_ifconfig == NULL )
		return -1;
	
	while( fgets( linebuf, 1000, p_ifconfig) )
	{
		char * p_inet = strnstr(linebuf, "inet ", 1000);
		if ( p_inet )
		{
			if ( strnstr( linebuf, "127.0.0.1", 1000 ) )
				continue;
			char * p_end = strchr( p_inet + 5, ' ' );
			if ( p_end == NULL )
				continue;
			*p_end = '\0';

			struct in_addr addr; 
			addr.s_addr = inet_addr( p_inet + 5 );
			if(addr.s_addr != INADDR_NONE)
				rtnvec.push_back(addr);
		}
	}
	RETURN(0);
#else
	//LINUX using gethostbyname maybe not get the address of DHCP, so just get address from netcard
	//getaddrinfo, maybe can use this function...

	int fd, intrface, retn = 0;       
	struct ifreq buf[16];       
	struct arpreq arp;       
	struct ifconf ifc; 

	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
	{
		ifc.ifc_len = sizeof(buf);       
		ifc.ifc_buf = (caddr_t) buf;       
		if (!ioctl(fd, SIOCGIFCONF, (char *) &ifc))   
		{   
			intrface = ifc.ifc_len/sizeof(struct ifreq);     
			//printf("interface num is intrface=%d\n", intrface);
			for(int i=0; i< intrface; i++)
			{   
				//printf("net device %s\n", buf[i].ifr_name);       
				/*Jugde whether the net card status is promisc     */       
				if (!(ioctl(fd, SIOCGIFFLAGS, (char *)&buf[i])))   
				{   
					if (buf[i].ifr_flags & IFF_PROMISC)   
					{   
						//puts("the interface is PROMISC");       
						retn++;   
					}   
				}   
				else   
				{   
					//char str[256];
					//sprintf(str,   "cpm: ioctl device %s", buf[intrface].ifr_name);       
					//perror(str);  
					continue;
				}

				/*Jugde whether the net card status is up */       
				//if (buf[i].ifr_flags & IFF_UP)   
				//{   
				//	puts("the   interface   status   is   UP");   
				//}   
				//else   
				//{   
				//	puts("the   interface   status   is   DOWN");   
				//}

				if (!(buf[i].ifr_flags & IFF_UP))
					continue;

				/*Get IP of the next card   */
				if (!(ioctl(fd, SIOCGIFADDR, (char *) &buf[i])))   
				{   
					//printf("IP address is: %s\n",inet_ntoa(((struct   sockaddr_in   *) (&buf[i].ifr_addr))->sin_addr));       
					//puts(buf[intrface].ifr_addr.sa_data);
					struct in_addr addr= (((struct sockaddr_in*) (&buf[i].ifr_addr))->sin_addr);
					if(inet_addr("127.0.0.1") != addr.s_addr)
						rtnvec.push_back(addr);
					//rtnvec.push_back(((struct sockaddr_in*) (&buf[i].ifr_addr))->sin_addr);

				}   
/*				else   
				{   
					char str[256];       

					sprintf(str, "cpm: ioctl device %s",   
					buf[intrface].ifr_name);       
					perror(str);   
				} */  

			   /*Get   HW   ADDRESS   of   the   net   card   */       
				//if   (!(ioctl(fd,   SIOCGIFHWADDR,   (char   *)   &buf[intrface])))   
				//{   
				//			puts("HW   address   is:");       

				//			printf("%02x:%02x:%02x:%02x:%02x:%02x\n",   
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[0],     
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[1],   
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[2],     
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[3],     
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[4],     
				//						(unsigned   char)   buf[intrface].ifr_hwaddr.sa_data[5]);   
				//			puts("");       
				//			puts("");   
				//}   
				//else   
				//{   
				//			char   str[256];       

				//			sprintf(str,   "cpm:   ioctl   device   %s",   buf[intrface].ifr_name);       

				//			perror(str);   
				//}   

			}
		}
		else
		{
			printf("ioctl error\n");
		}
	}
	return (0);
#endif
}

} }
